﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BricksProblemSolver.Solution2
{
    /// <summary>
    /// Use a 64bit to hold the brick row, it can support max row length of 64.
    /// Add brick from higher to lower bit.
    /// </summary>
    public class BrickRow2 : IBrickRow
    {
        private const ulong ULONG_ONE = 1;
        private ulong _bricksData;
        private int _length;
        private int _lastBrickPosition;

        public BrickRow2(int length)
        {
            if (length <= 0 || length > 64)
            {
                throw new ArgumentException(nameof(length), "The length should between 0 and 64.");
            }
            _length = length;
            _lastBrickPosition = length;
        }

        public int Length
        {
            get
            {
                return _length;
            }
        }

        public bool IsFull
        {
            get
            {
                return _lastBrickPosition == 0;
            }
        }

        public void AddBrick(int brickLength)
        {
            lock (this)
            {
                if (!CanAddBrick(brickLength))
                {
                    throw new InvalidOperationException(
                        string.Format("Cannot add brick of length {0}. Current total length is {1}.", brickLength, _lastBrickPosition));
                }
                _lastBrickPosition -= brickLength;
                _bricksData |= (((ulong)0x1) << _lastBrickPosition);
            }
        }

        public bool CanAddBrick(int brickLength)
        {
            return _lastBrickPosition - brickLength >= 0;
        }

        public bool HasNoGapOnSameLine(IBrickRow otherRow)
        {
            var compareRow = otherRow as BrickRow2;
            if (compareRow == null)
            {
                throw new ArgumentNullException("compareRow");
            }
            return (compareRow._bricksData & _bricksData) == ULONG_ONE;
        }

        public IEnumerable<sbyte> GetAllBricks()
        {
            sbyte brickLength = 0;
            for (int i = _length - 1; i >= 0; --i)
            {
                ++brickLength;
                if ((_bricksData & (ULONG_ONE << i)) != 0)
                {
                    yield return brickLength;
                    brickLength = 0;
                }
            }
        }

        public override int GetHashCode()
        {
            return (int)_bricksData;
        }

        public override bool Equals(object obj)
        {
            var other = obj as BrickRow2;
            if (other == null)
            {
                return false;
            }
            return other._bricksData == _bricksData;
        }

        public void Clear()
        {
            _bricksData = 0;
            _lastBrickPosition = _length;
        }

        public override string ToString()
        {
            var builder = new StringBuilder(Length + 1);
            for(int i = _length - 1; i >= 0; --i)
            {
                if ((_bricksData & (ULONG_ONE << i)) != 0)
                {
                    builder.Append("|");
                }
                else
                {
                    builder.Append("_");
                }
            }
            return builder.ToString();
        }

        public object Clone()
        {
            var row = new BrickRow2(_length);
            row._bricksData = _bricksData;
            row._lastBrickPosition = _lastBrickPosition;
            return row;
        }
    }
}
